<!DOCTYPE html>
<html lang="zh-CN" data-theme="light">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <meta name="generator" content="VuePress 2.0.0-beta.53" />
    <meta name="theme" content="VuePress Theme Hope" />
    <link rel="icon" href="/doc/images/favicon.ico"><title>第 5 章 类和接口 | AT 的文档库</title><meta name="description" content="At 的文档库，好记性不如烂笔头！">
    <style>
      :root {
        --bg-color: #fff;
      }

      html[data-theme="dark"] {
        --bg-color: #1d2025;
      }

      html,
      body {
        background: var(--bg-color);
      }
    </style>
    <script>
      const userMode = localStorage.getItem("vuepress-theme-hope-scheme");
      const systemDarkMode =
        window.matchMedia &&
        window.matchMedia("(prefers-color-scheme: dark)").matches;

      if (userMode === "dark" || (userMode !== "light" && systemDarkMode)) {
        document.querySelector("html").setAttribute("data-theme", "dark");
      }
    </script>
    <link rel="preload" href="/doc/assets/style.c3a4c10b.css" as="style" /><link rel="stylesheet" href="/doc/assets/style.c3a4c10b.css" />
    <link rel="modulepreload" href="/doc/assets/app.41440b49.js"><link rel="modulepreload" href="/doc/assets/第 5 章 类和接口.html.e392a0d1.js"><link rel="modulepreload" href="/doc/assets/_plugin-vue_export-helper.cdc0426e.js"><link rel="modulepreload" href="/doc/assets/第 5 章 类和接口.html.8988d546.js"><link rel="prefetch" href="/doc/assets/index.html.0bcc895f.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.c8df2167.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue2.html.48e096c5.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue3.html.324e646c.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.fb0ad0e0.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章 导言.html.d6c662f8.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章 TypeScript 概述.html.e976154a.js" as="script" /><link rel="prefetch" href="/doc/assets/第 3 章 类型全解.html.e4f1b931.js" as="script" /><link rel="prefetch" href="/doc/assets/第 4 章 函数.html.701a2968.js" as="script" /><link rel="prefetch" href="/doc/assets/第 6 章 类型进阶.html.45b313e3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a9dd6465.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a3374cf8.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.c326eaaf.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.88582229.js" as="script" /><link rel="prefetch" href="/doc/assets/常见问题.html.e0f65550.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展使用.html.87cccc41.js" as="script" /><link rel="prefetch" href="/doc/assets/掌握 GIT.html.c150b234.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.a3c2858b.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.9a53d157.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.101afd82.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.8224e1cd.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.54b5ca2d.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.d839cc53.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.80e4d5cc.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.9a178a04.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展知识.html.3535ab30.js" as="script" /><link rel="prefetch" href="/doc/assets/项目搭建.html.e926587b.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.881e95b0.js" as="script" /><link rel="prefetch" href="/doc/assets/安装及使用.html.ba7314a3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f9509ce0.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章.html.726a0bb9.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章.html.38f55e7d.js" as="script" /><link rel="prefetch" href="/doc/assets/Controllers.html.6b27764c.js" as="script" /><link rel="prefetch" href="/doc/assets/Module.html.3849b10b.js" as="script" /><link rel="prefetch" href="/doc/assets/Providers.html.7d2793f1.js" as="script" /><link rel="prefetch" href="/doc/assets/404.html.c5de7dd9.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.86b08b0b.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f4119260.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue2.html.caf7eb18.js" as="script" /><link rel="prefetch" href="/doc/assets/Vue3.html.6dc23cf4.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.868f2c80.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章 导言.html.9edf000e.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章 TypeScript 概述.html.76b27e4d.js" as="script" /><link rel="prefetch" href="/doc/assets/第 3 章 类型全解.html.9ad8a3d4.js" as="script" /><link rel="prefetch" href="/doc/assets/第 4 章 函数.html.9115df00.js" as="script" /><link rel="prefetch" href="/doc/assets/第 6 章 类型进阶.html.1b5b864e.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.e5a75968.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.974654cf.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.ed3fa7a3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.3b9ebcaa.js" as="script" /><link rel="prefetch" href="/doc/assets/常见问题.html.b427e39a.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展使用.html.76b3dd90.js" as="script" /><link rel="prefetch" href="/doc/assets/掌握 GIT.html.724416fc.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.8b55f9ef.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.8ee968a7.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.06371751.js" as="script" /><link rel="prefetch" href="/doc/assets/面试题详解.html.6b672044.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.b200eaff.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.5135d2c4.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.557b7bf5.js" as="script" /><link rel="prefetch" href="/doc/assets/基础掌握.html.7b535b09.js" as="script" /><link rel="prefetch" href="/doc/assets/扩展知识.html.c6b233af.js" as="script" /><link rel="prefetch" href="/doc/assets/项目搭建.html.450f0b1d.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.6483ed06.js" as="script" /><link rel="prefetch" href="/doc/assets/安装及使用.html.b4d859f3.js" as="script" /><link rel="prefetch" href="/doc/assets/index.html.f15cf3e9.js" as="script" /><link rel="prefetch" href="/doc/assets/第 1 章.html.127b1e7c.js" as="script" /><link rel="prefetch" href="/doc/assets/第 2 章.html.ea8e67e2.js" as="script" /><link rel="prefetch" href="/doc/assets/Controllers.html.029df1e7.js" as="script" /><link rel="prefetch" href="/doc/assets/Module.html.f102db2a.js" as="script" /><link rel="prefetch" href="/doc/assets/Providers.html.9bef3753.js" as="script" /><link rel="prefetch" href="/doc/assets/404.html.2cfd6b20.js" as="script" /><link rel="prefetch" href="/doc/assets/photoswipe.esm.09e03fed.js" as="script" />
  </head>
  <body>
    <div id="app"><!--[--><!--[--><!--[--><span tabindex="-1"></span><a href="#main-content" class="skip-link sr-only">Skip to content</a><!--]--><div class="theme-container has-toc"><!--[--><!--[--><header class="navbar"><div class="navbar-left"><button class="toggle-sidebar-button" title="Toggle Sidebar"><span class="icon"></span></button><!--[--><!----><!--]--><a href="/doc/" class="brand"><!----><!----><span class="site-name">AT 的文档库</span></a><!--[--><!----><!--]--></div><div class="navbar-center"><!--[--><!----><!--]--><nav class="nav-links"><div class="nav-item hide-in-mobile"><a href="/doc/" class="nav-link" aria-label="首页"><!---->首页<!----></a></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="开发问题汇总"><span class="title"><!---->开发问题汇总</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/development-question/Vue" class="nav-link" aria-label="Vue"><!---->Vue<!----></a></li></ul></button></div></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="系统笔记"><span class="title"><!---->系统笔记</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/sysnotes/Git/%E6%8E%8C%E6%8F%A1%20GIT.html" class="nav-link" aria-label="GIT"><!---->GIT<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/Nvm/%E5%AE%89%E8%A3%85%E5%8F%8A%E4%BD%BF%E7%94%A8.html" class="nav-link" aria-label="NVM"><!---->NVM<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/Linux/%E5%9F%BA%E7%A1%80%E6%8E%8C%E6%8F%A1.html" class="nav-link" aria-label="Linux"><!---->Linux<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/JavaScript" class="nav-link" aria-label="JavaScript"><!---->JavaScript<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/CSS" class="nav-link" aria-label="CSS"><!---->CSS<!----></a></li><li class="dropdown-item"><a href="/doc/sysnotes/HTML" class="nav-link" aria-label="HTML"><!---->HTML<!----></a></li></ul></button></div></div><div class="nav-item hide-in-mobile"><div class="dropdown-wrapper"><button class="dropdown-title" type="button" aria-label="阅读笔记"><span class="title"><!---->阅读笔记</span><span class="arrow"></span><ul class="nav-dropdown"><li class="dropdown-item"><a href="/doc/readbook/《Vue.js 设计与实现》/" class="nav-link" aria-label="《Vue.js 设计与实现》"><!---->《Vue.js 设计与实现》<!----></a></li><li class="dropdown-item"><a href="/doc/readbook/《TypeScript 编程》/" class="nav-link" aria-label="《TypeScript 编程》"><!---->《TypeScript 编程》<!----></a></li></ul></button></div></div></nav><!--[--><!----><!--]--></div><div class="navbar-right"><!--[--><!----><!--]--><div class="nav-item hide-in-mobile"><button id="appearance-switch"><svg xmlns="http://www.w3.org/2000/svg" class="icon auto-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="auto icon" style="display:block;"><path d="M512 992C246.92 992 32 777.08 32 512S246.92 32 512 32s480 214.92 480 480-214.92 480-480 480zm0-840c-198.78 0-360 161.22-360 360 0 198.84 161.22 360 360 360s360-161.16 360-360c0-198.78-161.22-360-360-360zm0 660V212c165.72 0 300 134.34 300 300 0 165.72-134.28 300-300 300z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon dark-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="dark icon" style="display:none;"><path d="M524.8 938.667h-4.267a439.893 439.893 0 0 1-313.173-134.4 446.293 446.293 0 0 1-11.093-597.334A432.213 432.213 0 0 1 366.933 90.027a42.667 42.667 0 0 1 45.227 9.386 42.667 42.667 0 0 1 10.24 42.667 358.4 358.4 0 0 0 82.773 375.893 361.387 361.387 0 0 0 376.747 82.774 42.667 42.667 0 0 1 54.187 55.04 433.493 433.493 0 0 1-99.84 154.88 438.613 438.613 0 0 1-311.467 128z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" class="icon light-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="light icon" style="display:none;"><path d="M952 552h-80a40 40 0 0 1 0-80h80a40 40 0 0 1 0 80zM801.88 280.08a41 41 0 0 1-57.96-57.96l57.96-58a41.04 41.04 0 0 1 58 58l-58 57.96zM512 752a240 240 0 1 1 0-480 240 240 0 0 1 0 480zm0-560a40 40 0 0 1-40-40V72a40 40 0 0 1 80 0v80a40 40 0 0 1-40 40zm-289.88 88.08-58-57.96a41.04 41.04 0 0 1 58-58l57.96 58a41 41 0 0 1-57.96 57.96zM192 512a40 40 0 0 1-40 40H72a40 40 0 0 1 0-80h80a40 40 0 0 1 40 40zm30.12 231.92a41 41 0 0 1 57.96 57.96l-57.96 58a41.04 41.04 0 0 1-58-58l58-57.96zM512 832a40 40 0 0 1 40 40v80a40 40 0 0 1-80 0v-80a40 40 0 0 1 40-40zm289.88-88.08 58 57.96a41.04 41.04 0 0 1-58 58l-57.96-58a41 41 0 0 1 57.96-57.96z"></path></svg></button></div><form class="search-box" role="search"><input type="search" autocomplete="off" spellcheck="false" value><!----></form><!--[--><!----><!--]--><button class="toggle-navbar-button" aria-label="Toggle Navbar" aria-expanded="false" aria-controls="nav-screen"><span class="button-container"><span class="button-top"></span><span class="button-middle"></span><span class="button-bottom"></span></span></button></div></header><!----><!--]--><!----><div class="toggle-sidebar-wrapper"><span class="arrow left"></span></div><aside class="sidebar"><!--[--><!----><!--]--><ul class="sidebar-links"><li><section class="sidebar-group"><button class="sidebar-heading clickable active"><!----><span class="title">《TypeScript 编程》</span><span class="arrow down"></span></button><ul class="sidebar-links"><li><!--[--><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%201%20%E7%AB%A0%20%E5%AF%BC%E8%A8%80.html" class="nav-link sidebar-link sidebar-page" aria-label="第 1 章 导言"><!---->第 1 章 导言<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%202%20%E7%AB%A0%20TypeScript%20%E6%A6%82%E8%BF%B0.html" class="nav-link sidebar-link sidebar-page" aria-label="第 2 章 TypeScript 概述"><!---->第 2 章 TypeScript 概述<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%203%20%E7%AB%A0%20%E7%B1%BB%E5%9E%8B%E5%85%A8%E8%A7%A3.html" class="nav-link sidebar-link sidebar-page" aria-label="第 3 章 类型全解"><!---->第 3 章 类型全解<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html" class="nav-link sidebar-link sidebar-page" aria-label="第 4 章 函数"><!---->第 4 章 函数<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li><li><!--[--><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html" class="router-link-active router-link-exact-active nav-link active sidebar-link sidebar-page active" aria-label="第 5 章 类和接口"><!---->第 5 章 类和接口<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-1-类和继承" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.1 类和继承"><!---->5.1 类和继承<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-2-super" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.2 super"><!---->5.2 super<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-3-以-this-为返回类型" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.3 以 this 为返回类型"><!---->5.3 以 this 为返回类型<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-4-接口" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.4 接口"><!---->5.4 接口<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-4-1-声明合并" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.4.1 声明合并"><!---->5.4.1 声明合并<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-4-2-实现" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.4.2 实现"><!---->5.4.2 实现<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-4-3-实现接口还是扩展抽象类" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.4.3 实现接口还是扩展抽象类"><!---->5.4.3 实现接口还是扩展抽象类<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-5-类是结构化类型" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.5 类是结构化类型"><!---->5.5 类是结构化类型<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-6-类既声名值也声明类型" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.6 类既声名值也声明类型"><!---->5.6 类既声名值也声明类型<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-7-多态" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.7 多态"><!---->5.7 多态<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-8-混入" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.8 混入"><!---->5.8 混入<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-9-装饰器" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.9 装饰器"><!---->5.9 装饰器<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-10-模拟-final-类" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.10 模拟 final 类"><!---->5.10 模拟 final 类<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-11-设计模式" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.11 设计模式"><!---->5.11 设计模式<!----></a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-11-1-工厂模式" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.11.1 工厂模式"><!---->5.11.1 工厂模式<!----></a><ul class="sidebar-sub-headers"></ul></li><li class="sidebar-sub-header"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-11-2-建造者模式" class="router-link-active router-link-exact-active nav-link sidebar-link heading" aria-label="5.11.2 建造者模式"><!---->5.11.2 建造者模式<!----></a><ul class="sidebar-sub-headers"></ul></li></ul></li></ul><!--]--></li><li><!--[--><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%206%20%E7%AB%A0%20%E7%B1%BB%E5%9E%8B%E8%BF%9B%E9%98%B6.html" class="nav-link sidebar-link sidebar-page" aria-label="第 6 章 类型进阶"><!---->第 6 章 类型进阶<!----></a><ul class="sidebar-sub-headers"></ul><!--]--></li></ul></section></li><li><section class="sidebar-group"><button class="sidebar-heading clickable"><!----><span class="title">《Vue.js 设计与实现》</span><span class="arrow right"></span></button><!----></section></li></ul><!--[--><!----><!--]--></aside><!--[--><main class="page" id="main-content"><!--[--><!----><nav class="breadcrumb disable"></nav><div class="page-title"><h1><!---->第 5 章 类和接口</h1><div class="page-info"><!----><!----><!----><!----><!----><span class="reading-time-info" aria-label="阅读时间⌛" data-balloon-pos="down"><svg xmlns="http://www.w3.org/2000/svg" class="icon timer-icon" viewBox="0 0 1024 1024" fill="currentColor" aria-label="timer icon"><path d="M799.387 122.15c4.402-2.978 7.38-7.897 7.38-13.463v-1.165c0-8.933-7.38-16.312-16.312-16.312H256.33c-8.933 0-16.311 7.38-16.311 16.312v1.165c0 5.825 2.977 10.874 7.637 13.592 4.143 194.44 97.22 354.963 220.201 392.763-122.204 37.542-214.893 196.511-220.2 389.397-4.661 5.049-7.638 11.651-7.638 19.03v5.825h566.49v-5.825c0-7.379-2.849-13.981-7.509-18.9-5.049-193.016-97.867-351.985-220.2-389.527 123.24-37.67 216.446-198.453 220.588-392.892zM531.16 450.445v352.632c117.674 1.553 211.787 40.778 211.787 88.676H304.097c0-48.286 95.149-87.382 213.728-88.676V450.445c-93.077-3.107-167.901-81.297-167.901-177.093 0-8.803 6.99-15.793 15.793-15.793 8.803 0 15.794 6.99 15.794 15.793 0 80.261 63.69 145.635 142.01 145.635s142.011-65.374 142.011-145.635c0-8.803 6.99-15.793 15.794-15.793s15.793 6.99 15.793 15.793c0 95.019-73.789 172.82-165.96 177.093z"></path></svg><span>大约 18 分钟</span><meta property="timeRequired" content="PT18M"></span></div><hr></div><div class="toc-place-holder"><aside id="toc"><div class="toc-header">此页内容</div><div class="toc-wrapper"><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-1-类和继承" class="router-link-active router-link-exact-active toc-link level2">5.1 类和继承</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-2-super" class="router-link-active router-link-exact-active toc-link level2">5.2 super</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-3-以-this-为返回类型" class="router-link-active router-link-exact-active toc-link level2">5.3 以 this 为返回类型</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-4-接口" class="router-link-active router-link-exact-active toc-link level2">5.4 接口</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-4-1-声明合并" class="router-link-active router-link-exact-active toc-link level3">5.4.1 声明合并</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-4-2-实现" class="router-link-active router-link-exact-active toc-link level3">5.4.2 实现</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-4-3-实现接口还是扩展抽象类" class="router-link-active router-link-exact-active toc-link level3">5.4.3 实现接口还是扩展抽象类</a></li><!----><!--]--></ul><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-5-类是结构化类型" class="router-link-active router-link-exact-active toc-link level2">5.5 类是结构化类型</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-6-类既声名值也声明类型" class="router-link-active router-link-exact-active toc-link level2">5.6 类既声名值也声明类型</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-7-多态" class="router-link-active router-link-exact-active toc-link level2">5.7 多态</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-8-混入" class="router-link-active router-link-exact-active toc-link level2">5.8 混入</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-9-装饰器" class="router-link-active router-link-exact-active toc-link level2">5.9 装饰器</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-10-模拟-final-类" class="router-link-active router-link-exact-active toc-link level2">5.10 模拟 final 类</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-11-设计模式" class="router-link-active router-link-exact-active toc-link level2">5.11 设计模式</a></li><ul class="toc-list"><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-11-1-工厂模式" class="router-link-active router-link-exact-active toc-link level3">5.11.1 工厂模式</a></li><!----><!--]--><!--[--><li class="toc-item"><a aria-current="page" href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%205%20%E7%AB%A0%20%E7%B1%BB%E5%92%8C%E6%8E%A5%E5%8F%A3.html#_5-11-2-建造者模式" class="router-link-active router-link-exact-active toc-link level3">5.11.2 建造者模式</a></li><!----><!--]--></ul><!--]--></ul></div></aside></div><!----><div class="theme-hope-content"><h1 id="第-5-章-类和接口" tabindex="-1"><a class="header-anchor" href="#第-5-章-类和接口" aria-hidden="true">#</a> 第 5 章 类和接口</h1><p>TypeScript 类大量借用了 C# 的相关理论，支持可见性修饰符、属性初始化语句、多态、装饰器和接口。</p><h2 id="_5-1-类和继承" tabindex="-1"><a class="header-anchor" href="#_5-1-类和继承" aria-hidden="true">#</a> 5.1 类和继承</h2><p>本节以国际象棋引擎为引：</p><p>提供一个 Api 供两个玩家交替走棋。</p><p>每个旗子都有颜色和当前位置。在国际象棋中，位置使用（字母+数字）坐标表示；字母沿 x 轴从左至右展开，数字沿着 y 轴从下到上展开。</p><img src="/doc/assets/5.1.d52a49d2.jpg" alt="棋盘" width="400px" style="display:block;margin:0 auto;"><p>国际向其中标准的代数标记法：A~H 称为“竖线”，1~8 称为“横线”。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">Color</span> <span class="token operator">=</span> <span class="token string">&quot;Black&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;White&quot;</span><span class="token punctuation">;</span> <span class="token comment">// 颜色</span>
<span class="token keyword">type</span> <span class="token class-name">Files</span> <span class="token operator">=</span> <span class="token string">&quot;A&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;B&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;C&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;D&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;E&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;F&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;G&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;H&quot;</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> <span class="token class-name">Rank</span> <span class="token operator">=</span> <span class="token number">1</span> <span class="token operator">|</span> <span class="token number">2</span> <span class="token operator">|</span> <span class="token number">3</span> <span class="token operator">|</span> <span class="token number">4</span> <span class="token operator">|</span> <span class="token number">5</span> <span class="token operator">|</span> <span class="token number">6</span> <span class="token operator">|</span> <span class="token number">7</span> <span class="token operator">|</span> <span class="token number">8</span><span class="token punctuation">;</span>

<span class="token comment">// 表示一次国际象棋游戏</span>
<span class="token keyword">class</span> <span class="token class-name">Game</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token comment">// 旗子</span>
<span class="token keyword">class</span> <span class="token class-name">Piece</span> <span class="token punctuation">{</span>
  <span class="token keyword">protected</span> position<span class="token operator">:</span> Postion<span class="token punctuation">;</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">private</span> <span class="token keyword">readonly</span> color<span class="token operator">:</span> Color<span class="token punctuation">,</span> file<span class="token operator">:</span> Files<span class="token punctuation">,</span> rank<span class="token operator">:</span> Rank<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>position <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Postion</span><span class="token punctuation">(</span>color<span class="token punctuation">,</span> file<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 旗子坐标</span>
<span class="token keyword">class</span> <span class="token class-name">Postion</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">private</span> file<span class="token operator">:</span> Files<span class="token punctuation">,</span> <span class="token keyword">private</span> rank<span class="token operator">:</span> Rank<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// 旗子类型</span>
<span class="token keyword">class</span> <span class="token class-name">King</span> <span class="token keyword">extends</span> <span class="token class-name">Piece</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">class</span> <span class="token class-name">Queen</span> <span class="token keyword">extends</span> <span class="token class-name">Piece</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">class</span> <span class="token class-name">Bishop</span> <span class="token keyword">extends</span> <span class="token class-name">Piece</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">class</span> <span class="token class-name">Knight</span> <span class="token keyword">extends</span> <span class="token class-name">Piece</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">class</span> <span class="token class-name">Rook</span> <span class="token keyword">extends</span> <span class="token class-name">Piece</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">class</span> <span class="token class-name">Pawn</span> <span class="token keyword">extends</span> <span class="token class-name">Piece</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol><li>颜色、横线和竖线相对较少，可以手动把可能的值使用类型字面量列举出来。限定了类型的范围，只能是全部字符串和全部数字中的莫欧协特定字符串和数字，进一步提升了安全型。</li><li>构造方法中的 <strong>private 访问修饰符</strong>自动把参数赋值给 <strong>this</strong>，并把可见性设为私有，意味着 Piece 实例中的代码可以读取和写入，但是 Piece 实例外部的代码不可以。不同的 Piece 实例访问各自的私有成员；其他类的实例，即便是 Piece 的子类也不可以访问私有成员。</li><li>把实例变量 position 的可见性声明为 <strong>protected</strong>。与 private 类似， protected 也是把属性赋值给 this，但是这样的属性对 Piece 和 Piece 子类的实例都可见。声明 position 时没有为其赋值，因此在 Piece 的构造方法中要赋值。如果未在构造方法中赋值，TypeScript 将提示变量没有明确赋值。也就是说，我们声明的类型是 T，但事实上是 T | undefined,因为没有在属性初始化语句或构造方法中为其赋值。因此需要更新属性的签名，指明其不一定是 Position 实例还有可能是 undefined。</li><li>new Piece 接受三个参数：color,file 和 rank。color 指定两个修饰符：private 将其赋值给 this,并确保只能有 Piece 的实例访问；另一个是 <strong>readonly</strong>，指明在初始赋值后，这个属性只能读取，不能修改。</li></ol><div class="custom-container tip"><p class="custom-container-title">TSC 标志：strictNullChecks 、strictPropertyInitialization</p><p>如果想检查类的实例变量有没有明确赋值，在 tsconfig 中启用 strictNullChecks 和 strictPropertyInitialzation。如果开启 strict 标志，这两个属性无需设置。</p></div><p>TypeScript 类中的属性和方法支持三个访问修饰符：</p><ul><li>public 任何地方都可以访问，默认访问级别</li><li>protected 可由当前类和其子类访问</li><li>private 只能由当前类的实例访问</li></ul><p><strong>访问修饰符的作用是不让类暴露过多的实现细节，而是只开放规范的 Api,供外部使用。</strong></p><p>定义类，但并不希望直接实例化，而是在此基础上扩展其他类，实例化其子类。可以使用 abstract 关键字进行限制：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token class-name">Piece</span> <span class="token punctuation">{</span>
  <span class="token keyword">protected</span> position<span class="token operator">:</span> Postion<span class="token punctuation">;</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">private</span> <span class="token keyword">readonly</span> color<span class="token operator">:</span> Color<span class="token punctuation">,</span> file<span class="token operator">:</span> Files<span class="token punctuation">,</span> rank<span class="token operator">:</span> Rank<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>position <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Postion</span><span class="token punctuation">(</span>color<span class="token punctuation">,</span> file<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码实例化 Piece，TypeScript 会报错。</p><p>abstract（抽象的） 关键字表名，不能直接实例化该类，但是并不阻止我们在类中定义方法。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token class-name">Piece</span> <span class="token punctuation">{</span>
  <span class="token keyword">protected</span> position<span class="token operator">:</span> Postion<span class="token punctuation">;</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">private</span> <span class="token keyword">readonly</span> color<span class="token operator">:</span> Color<span class="token punctuation">,</span> file<span class="token operator">:</span> Files<span class="token punctuation">,</span> rank<span class="token operator">:</span> Rank<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>position <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Postion</span><span class="token punctuation">(</span>color<span class="token punctuation">,</span> file<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">moveTo</span><span class="token punctuation">(</span>position<span class="token operator">:</span> Postion<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>position <span class="token operator">=</span> position<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">abstract</span> <span class="token function">canMoveTo</span><span class="token punctuation">(</span>position<span class="token operator">:</span> Postion<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Piece 类包含以下信息：</p><ul><li>告诉子类，子类必须实现一个名为 canMoveTo 的方法，而且要兼容指定的签名。如果扩展 Piece 的类忘记实现抽象的 canMoveTo 方法，编译是将提示类型错误。实现抽象类时也要实现抽象方法。</li><li>Piece 类为 moveTo 方法提供了默认实现（子类可覆盖默认实现方法）。没有为 moveTo 设置访问修饰符，默认 public。</li></ul><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">Color</span> <span class="token operator">=</span> <span class="token string">&quot;Black&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;White&quot;</span><span class="token punctuation">;</span> <span class="token comment">// 颜色</span>
<span class="token keyword">type</span> <span class="token class-name">Files</span> <span class="token operator">=</span> <span class="token string">&quot;A&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;B&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;C&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;D&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;E&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;F&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;G&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;H&quot;</span><span class="token punctuation">;</span>
<span class="token keyword">type</span> <span class="token class-name">Rank</span> <span class="token operator">=</span> <span class="token number">1</span> <span class="token operator">|</span> <span class="token number">2</span> <span class="token operator">|</span> <span class="token number">3</span> <span class="token operator">|</span> <span class="token number">4</span> <span class="token operator">|</span> <span class="token number">5</span> <span class="token operator">|</span> <span class="token number">6</span> <span class="token operator">|</span> <span class="token number">7</span> <span class="token operator">|</span> <span class="token number">8</span><span class="token punctuation">;</span>

<span class="token comment">// 旗子</span>
<span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token class-name">Piece</span> <span class="token punctuation">{</span>
  <span class="token keyword">protected</span> position<span class="token operator">:</span> Postion<span class="token punctuation">;</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> color<span class="token operator">:</span> Color<span class="token punctuation">,</span>
    <span class="token keyword">private</span> file<span class="token operator">:</span> Files<span class="token punctuation">,</span>
    <span class="token keyword">private</span> rank<span class="token operator">:</span> Rank
  <span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>position <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Postion</span><span class="token punctuation">(</span>file<span class="token punctuation">,</span> rank<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">moveTo</span><span class="token punctuation">(</span>position<span class="token operator">:</span> Postion<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>position <span class="token operator">=</span> position<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token keyword">abstract</span> <span class="token function">canMoveTo</span><span class="token punctuation">(</span>position<span class="token operator">:</span> Postion<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>

  <span class="token function">distanceFrom</span><span class="token punctuation">(</span>positon<span class="token operator">:</span> Postion<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span>
      rank<span class="token operator">:</span> Math<span class="token punctuation">.</span><span class="token function">abs</span><span class="token punctuation">(</span>positon<span class="token punctuation">.</span>rank <span class="token operator">-</span> <span class="token keyword">this</span><span class="token punctuation">.</span>rank<span class="token punctuation">)</span><span class="token punctuation">,</span>
      file<span class="token operator">:</span> Math<span class="token punctuation">.</span><span class="token function">abs</span><span class="token punctuation">(</span>positon<span class="token punctuation">.</span>file<span class="token punctuation">.</span><span class="token function">charCodeAt</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token keyword">this</span><span class="token punctuation">.</span>file<span class="token punctuation">.</span><span class="token function">charCodeAt</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// 旗子坐标</span>
<span class="token keyword">class</span> <span class="token class-name">Postion</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">public</span> file<span class="token operator">:</span> Files<span class="token punctuation">,</span> <span class="token keyword">public</span> rank<span class="token operator">:</span> Rank<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// 旗子类型</span>
<span class="token keyword">class</span> <span class="token class-name">King</span> <span class="token keyword">extends</span> <span class="token class-name">Piece</span> <span class="token punctuation">{</span>
  <span class="token function">canMoveTo</span><span class="token punctuation">(</span>position<span class="token operator">:</span> Postion<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
    <span class="token keyword">let</span> distance <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">distanceFrom</span><span class="token punctuation">(</span>position<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> distance<span class="token punctuation">.</span>rank <span class="token operator">&lt;</span> <span class="token number">2</span> <span class="token operator">&amp;&amp;</span> distance<span class="token punctuation">.</span>file <span class="token operator">&lt;</span> <span class="token number">2</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// 表示一次国际象棋游戏</span>
<span class="token keyword">class</span> <span class="token class-name">Game</span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> preces <span class="token operator">=</span> Game<span class="token punctuation">;</span>
  <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token function">makePiece</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token punctuation">[</span><span class="token keyword">new</span> <span class="token class-name">King</span><span class="token punctuation">(</span><span class="token string">&quot;Black&quot;</span><span class="token punctuation">,</span> <span class="token string">&quot;E&quot;</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">// class Queen extends Piece {}</span>
<span class="token comment">// class Bishop extends Piece {}</span>
<span class="token comment">// class Knight extends Piece {}</span>
<span class="token comment">// class Rook extends Piece {}</span>
<span class="token comment">// class Pawn extends Piece {}</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码对 TypeScript 类的用法了解甚多，总结如下：</p><ul><li>类使用 class 关键字申明。扩展类时使用 extends 关键字继承。</li><li>类可以使具体的，也可以是抽象的（abstract）。抽象类可以有抽象方法和抽象属性。</li><li>方法的可见性可以是：public、protected 或 private。方法分实例方法和静态方法。</li><li>类可以有实例属性，可见性也可以是 public、protected 或 private。实例属性可以在构造方法中的参数中声明，也可以用过属性初始化语句声明。</li><li>声明实例属性时可以使用 readonly 把属性标记为只读。</li></ul><h2 id="_5-2-super" tabindex="-1"><a class="header-anchor" href="#_5-2-super" aria-hidden="true">#</a> 5.2 super</h2><p>TypeScipt 也支持 super 调用。如果子类覆盖父类定义的方法，在子类中可使用 super 调用父类中的方法。super 有两种调用方法：</p><ol><li>方法调用：super.xxxx。</li><li>构造方法调用：使用特殊的形式 super()，而且只能在构造方法中调用，如果子类有构造方法闷在子类的构造方法中必须调用 super()，将子类和父类联系起来。</li></ol><div class="custom-container tip"><p class="custom-container-title">提示</p><p>使用 super 只能访问父类方法，不能访问父类属性。</p></div><h2 id="_5-3-以-this-为返回类型" tabindex="-1"><a class="header-anchor" href="#_5-3-以-this-为返回类型" aria-hidden="true">#</a> 5.3 以 this 为返回类型</h2><p>this可以作用值，还可以作用类型。对于类来说，this 类型还可以用于注解方法的返回类型。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name">Set</span> <span class="token punctuation">{</span>
  <span class="token function">has</span><span class="token punctuation">(</span>value<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">add</span><span class="token punctuation">(</span>value<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token operator">:</span> Set <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">MutableSet</span> <span class="token keyword">extends</span> <span class="token class-name">Set</span> <span class="token punctuation">{</span>
  <span class="token function">add</span><span class="token punctuation">(</span>value<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token operator">:</span> MutableSet <span class="token punctuation">{</span>
    
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码中：Set 类定义 add 方法，add 返回值类型是 Set 类型，此时 MutableSet 类继承 Set 类，如果不覆盖 add 方法，在调用 MutableSet 实例的 add 方法 返回依然是 Set。需要手动覆盖掉继承自 Set 的方法。可以在 Set 类中的 add 方法 返回 this:</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name">Set</span> <span class="token punctuation">{</span>
  <span class="token function">has</span><span class="token punctuation">(</span>value<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">boolean</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">add</span><span class="token punctuation">(</span>value<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">this</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">MutableSet</span> <span class="token keyword">extends</span> <span class="token class-name">Set</span> <span class="token punctuation">{</span>
   
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>这样减少了 add 代码的重写。因为 this 指向是当前调用类的实例。这样的对链式 Api 非常便利。</p><h2 id="_5-4-接口" tabindex="-1"><a class="header-anchor" href="#_5-4-接口" aria-hidden="true">#</a> 5.4 接口</h2><p>类经常当做接口使用。</p><p>与类型别名相似，接口是一种命名类型的方式，这样就不用再行内定义。类型别名和接口算是同一种概念的两种语法（类似于函数表达式和函数声明之间的关系），还是存在一定的区别。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">Sushi</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  calories<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
  salty<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>
  tasty<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">interface</span> <span class="token class-name">Sushi</span> <span class="token punctuation">{</span>
  calories<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
  salty<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>
  tasty<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>使用 Sushi 类型别名的地方都能使用 Sushi 接口。两个声明都定义结构，而且二者都可以相互赋值。 但是接口名和类型别名一样不能声明在同一文件中。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">Food</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  calories<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
  tasty<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">type</span> <span class="token class-name">Sushi</span> <span class="token operator">=</span> Food <span class="token operator">&amp;</span> <span class="token punctuation">{</span>
  salty<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">type</span> <span class="token class-name">Cake</span> <span class="token operator">=</span> Food <span class="token operator">&amp;</span> <span class="token punctuation">{</span>
  sweet<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment">// 接口实现</span>

<span class="token keyword">interface</span> <span class="token class-name">Food</span> <span class="token punctuation">{</span>
  calories<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">;</span>
  tasty<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">interface</span> <span class="token class-name">Sushi</span> <span class="token keyword">extends</span> <span class="token class-name">Food</span> <span class="token punctuation">{</span>
  salty<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">interface</span> <span class="token class-name">Cake</span> <span class="token keyword">extends</span> <span class="token class-name">Sushi</span> <span class="token punctuation">{</span>
  sweet<span class="token operator">:</span> <span class="token builtin">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>以上实现了类型别名和接口的继承类型。两种方式基本无差别。</p><div class="custom-container tip"><p class="custom-container-title">提示</p><p>接口不一定扩展其他接口。接口可以扩展任何类型：对象类型，类或其它接口。</p></div><p>类型和接口之间的区别：</p><ol><li>类型别名更为通用，右边可以是任何类型，包括类型表达式（类型，外加 &amp; 或 | 等类型运算符）；而在接口生命中，右边必须为结构。</li></ol><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name"><span class="token constant">A</span></span> <span class="token operator">=</span> <span class="token builtin">number</span>
<span class="token keyword">type</span> <span class="token class-name"><span class="token constant">B</span></span> <span class="token operator">=</span> <span class="token constant">A</span> <span class="token operator">|</span> <span class="token builtin">string</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码就不可以使用接口重写。</p><ol start="2"><li>扩展接口时，TypeScript 将检查扩展的接口是否可赋值给被扩展的接口。</li></ol><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">interface</span> <span class="token class-name"><span class="token constant">A</span></span> <span class="token punctuation">{</span>
  <span class="token function">good</span><span class="token punctuation">(</span>x<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
  <span class="token function">bad</span><span class="token punctuation">(</span>x<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">interface</span> <span class="token class-name"><span class="token constant">B</span></span> <span class="token keyword">extends</span> <span class="token class-name"><span class="token constant">A</span></span> <span class="token punctuation">{</span>
  <span class="token function">good</span><span class="token punctuation">(</span>x<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token operator">|</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
  <span class="token function">bad</span><span class="token punctuation">(</span>x<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码中，bad 方法类型对参数类型进行重写。TypeScript 会检测报错，并编译错误。而是使用类型别名的交集（&amp;）时，TypeScript 会把扩展的和被扩展的组合到一起，最终结果是 重载 bad 的签名，而不会抛出编译错误。</p><p>建模对象类型的继承时，TypeScript 对接口所做的可赋值性检查是捕获错误的有力工具。</p><ol start="3"><li>同一作用域总的多个同名接口将自动合并；同一作用域的多个同名类型别名将导致编译错误。这个特性成为声明合并。</li></ol><h3 id="_5-4-1-声明合并" tabindex="-1"><a class="header-anchor" href="#_5-4-1-声明合并" aria-hidden="true">#</a> 5.4.1 声明合并</h3><p>声明合并指 TypeScript 自动把多个同名声明组合在一起。</p><p>例如声明两个 User 接口类型， TypeScript 会自动将其合并。使用类型别名的话 TypeScript 会报错。</p><p>两个接口不能有冲突，如果一个接口中的某个属性类型为 T，另一个接口的相同属性名的类型为 U，由于 T 和 U 不是同一种类型，TypeScript 会提示错误。</p><p>如果接口中声明了泛型，那么两个接口中要使用完全相同的方式声明泛型（名称一样不行），才能合并接口。</p><h3 id="_5-4-2-实现" tabindex="-1"><a class="header-anchor" href="#_5-4-2-实现" aria-hidden="true">#</a> 5.4.2 实现</h3><p>声明类型时，可以使用 implements 关键字指明该类满足某接口。与显示注解一样，为类添加类型层面约束的一种方式。能保证泪在实现上的正确性，防止错误出现在下游，不止具体原因。这也是实现常用的设计模式（例如适配器、工厂和策略）的一种常见方式。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">interface</span> <span class="token class-name">Animal</span> <span class="token punctuation">{</span>
  <span class="token function">eat</span><span class="token punctuation">(</span>food<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
  <span class="token function">sleep</span><span class="token punctuation">(</span>hours<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Cat</span> <span class="token keyword">implements</span> <span class="token class-name">Animal</span> <span class="token punctuation">{</span>
  <span class="token function">eat</span><span class="token punctuation">(</span>food<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
    <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>food<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">sleep</span><span class="token punctuation">(</span>hours<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
    <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>hours<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码：Cat 类必须实现 Animal 生命的每一个方法。</p><p>接口可以声明实例属性，但是不能带有可见性的修饰符（private,protedced,public）,也不能使用 static 关键字。可以使用 readonly 把实例属性标记为只读：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">interface</span> <span class="token class-name">Animal</span> <span class="token punctuation">{</span>
  <span class="token keyword">readonly</span> name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
  <span class="token function">eat</span><span class="token punctuation">(</span>food<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
  <span class="token function">sleep</span><span class="token punctuation">(</span>hours<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Cat</span> <span class="token keyword">implements</span> <span class="token class-name">Animal</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">readonly</span> name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">eat</span><span class="token punctuation">(</span>food<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
    <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>food<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">sleep</span><span class="token punctuation">(</span>hours<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
    <span class="token builtin">console</span><span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>hours<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>一个类不限于只能实现一个接口：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">interface</span> <span class="token class-name">Animal</span> <span class="token punctuation">{</span>
  <span class="token keyword">readonly</span> name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
  <span class="token function">eat</span><span class="token punctuation">(</span>food<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
  <span class="token function">sleep</span><span class="token punctuation">(</span>hours<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">interface</span> <span class="token class-name">Feline</span> <span class="token punctuation">{</span>
  <span class="token function">meow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Cat</span> <span class="token keyword">implements</span> <span class="token class-name">Animal</span><span class="token punctuation">,</span> Feline <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">readonly</span> name<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">eat</span><span class="token punctuation">(</span>food<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">sleep</span><span class="token punctuation">(</span>hours<span class="token operator">:</span> <span class="token builtin">number</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">meow</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="_5-4-3-实现接口还是扩展抽象类" tabindex="-1"><a class="header-anchor" href="#_5-4-3-实现接口还是扩展抽象类" aria-hidden="true">#</a> 5.4.3 实现接口还是扩展抽象类</h3><p>实现接口与扩展抽象类差不多。区别是，接口更通用、更轻量，而抽象类的作用更具体、功能更丰富。</p><p>接口是对结构建模的方式。在值层面可表示对象、数组、类或类的实例。接口不生成 JavaScript 代码，只存在于编译时。</p><p>抽象类只能对类建模，而且生成运行时代码，即 JavaScript 类。抽象类可以有构造方法，可以提供默认实现，还能为属性和方法设置访问修饰符。接口无法实现。</p><p>如果多个类公用同一个实现，使用抽象类。如果需要一种轻量的方式表示，使用接口。</p><h2 id="_5-5-类是结构化类型" tabindex="-1"><a class="header-anchor" href="#_5-5-类是结构化类型" aria-hidden="true">#</a> 5.5 类是结构化类型</h2><p>TypeScript 根据结构比较类，与类的名称无关，类与其他类型是否兼容，要看结构；如果常规的对象定义了同样的属性或方法，也兼容类。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name">Zebra</span> <span class="token punctuation">{</span>
  <span class="token function">trot</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Poole</span> <span class="token punctuation">{</span>
  <span class="token function">trot</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function">ambleAround</span><span class="token punctuation">(</span>animal<span class="token operator">:</span> Zebra<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token keyword">let</span> zebra <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Zebra</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> poole <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Poole</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">ambleAround</span><span class="token punctuation">(</span>poole<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码中：Zebra 和 Poole 分别为两个类，但是都实现了 trot 方法，TypeScript 在验证的时候只会去对比结构。验证了 TypeScript 是彻底的结构化类型语言。</p><p>但是，如果类中有使用 private 或 proctected 修饰字段，而且结构不是类或其子类的实例，那么结构就不可赋值给类。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name"><span class="token constant">A</span></span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> name <span class="token operator">=</span> <span class="token string">&quot;zs&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name"><span class="token constant">B</span></span> <span class="token keyword">extends</span> <span class="token class-name"><span class="token constant">A</span></span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function">fun</span><span class="token punctuation">(</span>a<span class="token operator">:</span> <span class="token constant">A</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token function">fun</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name"><span class="token constant">A</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">fun</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name"><span class="token constant">B</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token function">fun</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name<span class="token operator">:</span> <span class="token string">&quot;zs&quot;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// ERROR</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>上述代码中：A 类有一个私有属性 name，B 类继承自 A 类。fun 函数接收参数为 A 类的实例对象或继承 A 类的实例对象。但是如果传入与 A 类实例对象结构相同的对象，因为有 private 修饰符，导致 TypeScript 无法通过编译。</p><h2 id="_5-6-类既声名值也声明类型" tabindex="-1"><a class="header-anchor" href="#_5-6-类既声名值也声明类型" aria-hidden="true">#</a> 5.6 类既声名值也声明类型</h2><p>在 TypeScript 中，类型和值位于不同的命名空间。根据场合，TypeScript 知道要使用的是类型还是值。</p><p>类和枚举在类型命名空间中生成类型，也在值命名空间中生成值。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name"><span class="token constant">C</span></span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token keyword">let</span> c<span class="token operator">:</span> <span class="token constant">C</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name"><span class="token constant">C</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">enum</span> <span class="token constant">E</span> <span class="token punctuation">{</span>
  <span class="token constant">F</span><span class="token punctuation">,</span>
  <span class="token constant">G</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span>

<span class="token keyword">let</span> e<span class="token operator">:</span> <span class="token constant">E</span> <span class="token operator">=</span> <span class="token constant">E</span><span class="token punctuation">.</span><span class="token constant">F</span><span class="token punctuation">;</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>由此可见，C 类和枚举 E 既能当作类型也能当做值使用。</p><p>C 指 C类的一个实例，如果要表示 C 类自身的类型，需要使用 typeof 关键字（TypeScript 提供的类型运算符，作用类似于 JavaScript 中值层面的 typeof，但是操作的是类型）</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">State</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token punctuation">[</span>key<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">]</span><span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">class</span> <span class="token class-name">StrignDatabase</span> <span class="token punctuation">{</span>
  state<span class="token operator">:</span> State <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token function">get</span><span class="token punctuation">(</span>key<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">string</span> <span class="token operator">|</span> <span class="token keyword">null</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> key <span class="token keyword">in</span> <span class="token keyword">this</span><span class="token punctuation">.</span>state <span class="token operator">?</span> <span class="token keyword">this</span><span class="token punctuation">.</span>state<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">set</span><span class="token punctuation">(</span>key<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> value<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>state<span class="token punctuation">[</span>key<span class="token punctuation">]</span> <span class="token operator">=</span> value<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">static</span> <span class="token function">from</span><span class="token punctuation">(</span>state<span class="token operator">:</span> State<span class="token punctuation">)</span><span class="token operator">:</span> StrignDatabase <span class="token punctuation">{</span>
    <span class="token keyword">let</span> db <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StrignDatabase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> key <span class="token keyword">in</span> state<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      db<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> state<span class="token punctuation">[</span>key<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    <span class="token keyword">return</span> db<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>StringDatabase 类声明成的类型：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">interface</span> <span class="token class-name">StrignDatabase</span> <span class="token punctuation">{</span>
  state<span class="token operator">:</span> State<span class="token punctuation">;</span>
  <span class="token function">get</span><span class="token punctuation">(</span>key<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">string</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  <span class="token function">set</span><span class="token punctuation">(</span>key<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">,</span> value<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>以及构造方法类型：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">interface</span> <span class="token class-name">StrignDatabaseConstructor</span> <span class="token punctuation">{</span>
  <span class="token keyword">new</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> StrignDatabase<span class="token punctuation">;</span>
  <span class="token function">from</span><span class="token punctuation">(</span>state<span class="token operator">:</span> State<span class="token punctuation">)</span><span class="token operator">:</span> StrignDatabase<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>即 StringDatabaseConstructor 只有一个方法 from，使用 new 运算符操作这个构造方法得到一个 StringDatabase 实例。这两个接口组合在一起对类的构造方法和实例进行建模。</p><p>new() 被称为构造方法签名，TypeScript 通过这种方式表示指定类型可以使用 new 运算符实例化。</p><p>也可以声明接收参数的构造方法：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name">StrignDatabase</span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">public</span> state<span class="token operator">:</span> State <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// 构造方法类型</span>
<span class="token keyword">interface</span> <span class="token class-name">StrignDatabaseConstructor</span> <span class="token punctuation">{</span>
  <span class="token keyword">new</span> <span class="token punctuation">(</span>state<span class="token operator">?</span><span class="token operator">:</span> State<span class="token punctuation">)</span><span class="token operator">:</span> StrignDatabase<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>综上，类声明不仅在值层面和类型层面生成相关内容，而且在类型层面生成两部分内容：一部分表示类的实例，另一部分表示构造方法（通过类型运算符 typeof 获取）</p><h2 id="_5-7-多态" tabindex="-1"><a class="header-anchor" href="#_5-7-多态" aria-hidden="true">#</a> 5.7 多态</h2><p>与函数和类型一样，类的接口对泛型参数也有深层支持，包括默认类型和限制，泛型的作用域可以放在整个类或接口中，也可以放在特定的方法中。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name">MyMap<span class="token operator">&lt;</span><span class="token constant">K</span><span class="token punctuation">,</span> <span class="token constant">V</span><span class="token operator">&gt;</span></span> <span class="token punctuation">{</span>
  <span class="token function">constructor</span><span class="token punctuation">(</span>initiaKey<span class="token operator">:</span> <span class="token constant">K</span><span class="token punctuation">,</span> initiaValue<span class="token operator">:</span> <span class="token constant">V</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">get</span><span class="token punctuation">(</span>key<span class="token operator">:</span> <span class="token constant">K</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token function">set</span><span class="token punctuation">(</span>key<span class="token operator">:</span> <span class="token constant">K</span><span class="token punctuation">,</span> value<span class="token operator">:</span> <span class="token constant">V</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token generic-function"><span class="token function">merge</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">K1</span><span class="token punctuation">,</span> <span class="token constant">V1</span><span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span>map<span class="token operator">:</span> MyMap<span class="token operator">&lt;</span><span class="token constant">K1</span><span class="token punctuation">,</span> <span class="token constant">V1</span><span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> MyMap<span class="token operator">&lt;</span><span class="token constant">K</span> <span class="token operator">|</span> <span class="token constant">K1</span><span class="token punctuation">,</span> <span class="token constant">V</span> <span class="token operator">|</span> <span class="token constant">V1</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token comment">// return new MyMap();</span>
  <span class="token punctuation">}</span>
  <span class="token keyword">static</span> <span class="token keyword">of</span><span class="token operator">&lt;</span><span class="token constant">K</span><span class="token punctuation">,</span> <span class="token constant">V</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>k<span class="token operator">:</span> <span class="token constant">K</span><span class="token punctuation">,</span> v<span class="token operator">:</span> <span class="token constant">V</span><span class="token punctuation">)</span><span class="token operator">:</span> MyMap<span class="token operator">&lt;</span><span class="token constant">K</span><span class="token punctuation">,</span> <span class="token constant">V</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul><li>声明类时绑定作用域为整个类型的泛型。K 和 V 在 MyMap 的每个实例方法和实力属性中都可用。</li><li>在构造函数中不能声明泛型，应该在类声明中声明泛型。</li><li>在类内部，任何地方都能使用作用域为整个类的泛型。</li><li>实例方法可以访问类一级的泛型，而且自己也可以声明泛型。merge 方法使用了类以及反省 K 和 V，而且自己还声明了两个泛型 K1 和 V1。</li><li>静态方法不能访问类的泛型，类似和值一样，不能访问类的实例变量。of 不能访问类中的 K 和 V，不过可以自己声明泛型。</li></ul><p>接口也可以绑定泛型：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">interface</span> <span class="token class-name">MyMap<span class="token operator">&lt;</span><span class="token constant">K</span><span class="token punctuation">,</span> <span class="token constant">V</span><span class="token operator">&gt;</span></span> <span class="token punctuation">{</span>
  <span class="token function">get</span><span class="token punctuation">(</span>key<span class="token operator">:</span> <span class="token constant">K</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">V</span><span class="token punctuation">;</span>
  <span class="token function">set</span><span class="token punctuation">(</span>key<span class="token operator">:</span> <span class="token constant">K</span><span class="token punctuation">,</span> value<span class="token operator">:</span> <span class="token constant">V</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>和函数一样，可以显示为泛型绑定具体类型，也可以让 TypeScript 自动推导：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">let</span> myMap <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyMap</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> myMap2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyMap<span class="token operator">&lt;</span><span class="token builtin">number</span><span class="token punctuation">,</span> <span class="token builtin">boolean</span><span class="token operator">&gt;</span></span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
myMap2<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">&quot;A&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 此时会报类型错误</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="_5-8-混入" tabindex="-1"><a class="header-anchor" href="#_5-8-混入" aria-hidden="true">#</a> 5.8 混入</h2><p>JavaScript 和 TypeScript 都没有 trait 或 mixin 关键字，可以使用<strong>继承</strong>实现混入。这两个特性都用于模拟多重继承（一个类扩展两个以上的类），可做面向对象编程。这种编程风格中，不表述“这是一个 Shape”,而是描述事物的属性，表述“这个东西可以度量”或“这个东西有四条边”；不再关心“是什么”关系，转而描述“能做什么”和“有什么”关系。</p><p>混入模式把行为和属性混合到类中。有以下特性：</p><ul><li>可以有状态（即实例属性）</li><li>只能提供具体方法（与抽象方法相反）</li><li>可以有构造方法，调用顺序与混入类的顺序一致</li></ul><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name">User</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

<span class="token keyword">type</span> <span class="token class-name">ClassConstructor</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token generic-function"><span class="token function">withEzDebug</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">C</span> <span class="token keyword">extends</span> ClassConstructor<span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span>Class<span class="token operator">:</span> <span class="token constant">C</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">class</span> <span class="token class-name"><span class="token keyword">extends</span></span> Class <span class="token punctuation">{</span>
    <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">super</span><span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ol><li>先声明 ClassConstructor 类型，表示任意构造方法。由于 TypeScript 完全采用结构化类型，因此可以使用 new 运算符操作的就是构造方法。不知道构造方法接受什么类型参数，所以指明可以接受任意类型参数。</li><li>声明 withEzDebug 混入，只接受一个类型参数 C。C 至少是类构造方法，使用 extends 表示。让 TypeScript 推导返回类型，结果是 C 与匿名类的交集。</li><li>由于混入是接受一个构造方法并返回一个构造方法的函数，所以返回一个匿名类构造方法。</li><li>类构造方法至少接受传入的类接受的参数。</li><li>与常规的 JavaScript 类一样，如果构造方法中没有什么逻辑，可以省略构造函数。</li></ol><h2 id="_5-9-装饰器" tabindex="-1"><a class="header-anchor" href="#_5-9-装饰器" aria-hidden="true">#</a> 5.9 装饰器</h2><p>装饰器是 TypeScript 的一个实验特性，为类、类方法、属性和方法参数的猿编程提供简洁的句法。其实，装饰器就是在装饰目标上调用函数的一种句法。</p><div class="custom-container tip"><p class="custom-container-title">TSC 标志：experimentalDecorators</p><p>如果想使用这个特性，在 tsconfig.json 中设置 experimentalDecorators:true</p></div><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token decorator"><span class="token at operator">@</span><span class="token function">serializable</span></span>
<span class="token keyword">class</span> <span class="token class-name">APIPayload</span> <span class="token punctuation">{</span>
  <span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Payload <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>@serializable 类装饰器对 APIPayload 进行包装，可以返回一个新类，替代原类。不使用装饰器：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">const</span> APIPayload <span class="token operator">=</span> <span class="token function">serializable</span><span class="token punctuation">(</span>
  <span class="token keyword">class</span> <span class="token class-name">APIPayload</span> <span class="token punctuation">{</span>
    <span class="token function">getVal</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>对不同的类的装饰器，TypeScript 要求作用域中有那种装饰器指定名称的函数，而且该函数还要具有相应的签名。</p><table><thead><tr><th>装饰目标</th><th>具有的类型签名</th></tr></thead><tbody><tr><td>类</td><td>(Constructor: {new (...any[]) =&gt; any } =&gt; any)</td></tr><tr><td>方法</td><td>(classPrototype: {}, methodName: string, descriptor: PropertyDescriptor) =&gt; any</td></tr><tr><td>静态方法</td><td>(Constructor: {new (...any[]) =&gt; any}, methodName:string, descriptor: PropertyDescriptor) =&gt; any</td></tr><tr><td>方法的参数</td><td>(classPrototype: {}, paramName: string, index: number) =&gt; void</td></tr><tr><td>静态方法的参数</td><td>(Constructor:{new (...any[]) =&gt; any}, paramName: string, index: number) =&gt; void</td></tr><tr><td>属性</td><td>(classPrototype:{},propertyName:string) =&gt; any</td></tr><tr><td>静态属性</td><td>(Constructor:{new (...any[]) =&gt; any}, propertyName:string) =&gt; any</td></tr><tr><td>属性设置方法/读值方法</td><td>(classProperty:{},propertyName:string,descriptor:PropertyDescriptor) =&gt; any</td></tr><tr><td>静态属性设置方法/读值方法</td><td>(Constructor:{new (...any[]) =&gt; any},propertyName:string,descriptor:PropertyDescriptor) =&gt; any</td></tr></tbody></table><p>TypeScript 没有内置任何装饰器。不同种类的装饰器（包括类装饰器、方法装饰器、属性装饰器和函数参数装饰器）都是常规的函数，只不过满足相应的特定签名。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">type</span> <span class="token class-name">ClassConstructor<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span></span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token punctuation">(</span><span class="token operator">...</span>args<span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token builtin">any</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token generic-function"><span class="token function">serializable</span><span class="token generic class-name"><span class="token operator">&lt;</span>
  <span class="token constant">T</span> <span class="token keyword">extends</span> ClassConstructor<span class="token operator">&lt;</span><span class="token punctuation">{</span>
    <span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token operator">&gt;</span>
<span class="token operator">&gt;</span></span></span><span class="token punctuation">(</span>Constructor<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token keyword">class</span> <span class="token class-name"><span class="token keyword">extends</span></span> Constructor <span class="token punctuation">{</span>
    <span class="token function">serialize</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token decorator"><span class="token at operator">@</span><span class="token function">serializable</span></span>
<span class="token keyword">class</span> <span class="token class-name">APIPayload</span> <span class="token punctuation">{</span>
  <span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul><li>在 TypeScript 中，类的构造方法使用 new() 表示结构化类型。如果类的构造方法可被扩展（使用 extends），TypeScript 要求参数的类型为可展开的 any 即 new(...any[])</li><li>@serializable 可以装饰任何实现了 getValue 方法</li><li>类装饰器是一个接受单个参数（目标类）的函数。如果装饰器函数返回一个类，在运行时这个类将替代被装饰的类；否则，要返回原类</li><li>为了装饰目标类，返回一个扩展原类的类，增加 serialize 方法。</li></ul><h2 id="_5-10-模拟-final-类" tabindex="-1"><a class="header-anchor" href="#_5-10-模拟-final-类" aria-hidden="true">#</a> 5.10 模拟 final 类</h2><p>TypeSript 的类和方法不支持 final 关键字，final 的作用：某些语言使用此关键字把类标记为不可扩展，或者把方法标记为不可覆盖。</p><p>在 TypeScript 中可以使用私有的构造函数模拟 final 类：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name">MessageQueue</span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">private</span> message<span class="token operator">:</span><span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>把 constructor 标记为 private 后，不能使用 new 运算符实例化类，也不能扩展类：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name">BadQueue</span> <span class="token keyword">extends</span> <span class="token class-name">MessageQueue</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token keyword">new</span> <span class="token class-name">MessageQueue</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>以上两种方式会报错。</p><p>如果只需要禁止扩展而不需要禁止实例化，可以借助于 static 静态方法实现：</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name">MessageQueue</span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> <span class="token function">constructor</span><span class="token punctuation">(</span><span class="token keyword">private</span> message<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

  <span class="token keyword">static</span> <span class="token function">create</span><span class="token punctuation">(</span>message<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">MessageQueue</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

MessageQueue<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="_5-11-设计模式" tabindex="-1"><a class="header-anchor" href="#_5-11-设计模式" aria-hidden="true">#</a> 5.11 设计模式</h2><h3 id="_5-11-1-工厂模式" tabindex="-1"><a class="header-anchor" href="#_5-11-1-工厂模式" aria-hidden="true">#</a> 5.11.1 工厂模式</h3><p>工厂模式（factory pattern）是创建某种类型对象的一种方式，这种方式把创建那种具体对象留给创建该对象的工厂决定。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token comment">// 也可以使用 interface</span>
<span class="token keyword">type</span> <span class="token class-name">Shop</span> <span class="token operator">=</span> <span class="token punctuation">{</span>
  purpose<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">class</span> <span class="token class-name">BalletFlat</span> <span class="token keyword">implements</span> <span class="token class-name">Shop</span> <span class="token punctuation">{</span>
  purpose <span class="token operator">=</span> <span class="token string">&quot;dancing&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Boot</span> <span class="token keyword">implements</span> <span class="token class-name">Shop</span> <span class="token punctuation">{</span>
  purpose <span class="token operator">=</span> <span class="token string">&quot;woodcutting&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">class</span> <span class="token class-name">Sneaker</span> <span class="token keyword">implements</span> <span class="token class-name">Shop</span> <span class="token punctuation">{</span>
  purpose <span class="token operator">=</span> <span class="token string">&quot;walking&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">let</span> Shoe <span class="token operator">=</span> <span class="token punctuation">{</span>
  <span class="token function">create</span><span class="token punctuation">(</span>type<span class="token operator">:</span> <span class="token string">&quot;balletFlat&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;boot&quot;</span> <span class="token operator">|</span> <span class="token string">&quot;sneaker&quot;</span><span class="token punctuation">)</span><span class="token operator">:</span> Shop <span class="token punctuation">{</span>
    <span class="token keyword">switch</span> <span class="token punctuation">(</span>type<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">case</span> <span class="token string">&quot;balletFlat&quot;</span><span class="token operator">:</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">BalletFlat</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">case</span> <span class="token string">&quot;boot&quot;</span><span class="token operator">:</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Boot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">case</span> <span class="token string">&quot;sneaker&quot;</span><span class="token operator">:</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Sneaker</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><ul><li>把 type 的值指定为一个并集类型有助于提升 create 的类型安全，以免在使用时传入无效 type</li><li>使用 switch 检查 type 值，确保处理每一处 Shoe</li></ul><p>这个示例使用伴生对象模式声明类型 Shoe 和同名的值 Shoe,以此表名值提供了操作类型的方法。使用 <code>Shoe.create(&#39;boot&#39;)</code></p><h3 id="_5-11-2-建造者模式" tabindex="-1"><a class="header-anchor" href="#_5-11-2-建造者模式" aria-hidden="true">#</a> 5.11.2 建造者模式</h3><p>建造者模式（builder pattern）把对象的建造方式与具体的实现方式区分开，JQuery，或者 ES6 的 Map 和 Set 等数据结构就是如此。</p><div class="language-typescript line-numbers-mode" data-ext="ts"><pre class="language-typescript"><code><span class="token keyword">class</span> <span class="token class-name">RequestBuilder</span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> url<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token operator">|</span> <span class="token keyword">null</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  <span class="token keyword">private</span> methods<span class="token operator">:</span> <span class="token builtin">string</span> <span class="token operator">|</span> <span class="token keyword">null</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  <span class="token keyword">private</span> data<span class="token operator">:</span> object <span class="token operator">|</span> <span class="token keyword">null</span> <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  <span class="token function">setUrl</span><span class="token punctuation">(</span>url<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>url <span class="token operator">=</span> url<span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">setMethods</span><span class="token punctuation">(</span>methods<span class="token operator">:</span> <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>methods <span class="token operator">=</span> methods<span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">setData</span><span class="token punctuation">(</span>data<span class="token operator">:</span> object<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>data <span class="token operator">=</span> data<span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
  <span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">new</span> <span class="token class-name">RequestBuilder</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setUrl</span><span class="token punctuation">(</span><span class="token string">&quot;/&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setMethods</span><span class="token punctuation">(</span><span class="token string">&quot;post&quot;</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setData</span><span class="token punctuation">(</span><span class="token punctuation">{</span> id<span class="token operator">:</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

</code></pre><div class="line-numbers" aria-hidden="true"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><div class="custom-container tip"><p class="custom-container-title">提示</p><p>传统的建造者模式不彻底安全：有可能在设定方法，URL 和数据之前调用 send，导致运行时异常。</p></div></div><!----><footer class="page-meta"><!----><!----><!----></footer><nav class="page-nav"><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%204%20%E7%AB%A0%20%E5%87%BD%E6%95%B0.html" class="nav-link prev" aria-label="第 4 章 函数"><div class="hint"><span class="arrow left"></span>上一页</div><div class="link"><!---->第 4 章 函数</div></a><a href="/doc/readbook/%E3%80%8ATypeScript%E7%BC%96%E7%A8%8B%E3%80%8B/%E7%AC%AC%206%20%E7%AB%A0%20%E7%B1%BB%E5%9E%8B%E8%BF%9B%E9%98%B6.html" class="nav-link next" aria-label="第 6 章 类型进阶"><div class="hint">下一页<span class="arrow right"></span></div><div class="link">第 6 章 类型进阶<!----></div></a></nav><!----><!----><!--]--></main><!--]--><!----><!--]--></div><!--]--><!----><!--]--></div>
    <script type="module" src="/doc/assets/app.41440b49.js" defer></script>
  </body>
</html>
